home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / apt / debfile.py < prev    next >
Encoding:
Python Source  |  2009-03-30  |  19.0 KB  |  540 lines

  1. # Copyright (c) 2005-2007 Canonical
  2. #
  3. # AUTHOR:
  4. # Michael Vogt <mvo@ubuntu.com>
  5. #
  6. # This file is part of GDebi
  7. #
  8. # GDebi is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License as published
  10. # by the Free Software Foundation; either version 2 of the License, or (at
  11. # your option) any later version.
  12. #
  13. # GDebi is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. # General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with GDebi; if not, write to the Free Software
  20. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21. #
  22. """Classes for working with locally available Debian packages."""
  23. from gettext import gettext as _
  24. import os
  25. import sys
  26.  
  27. import apt_inst
  28. import apt_pkg
  29.  
  30.  
  31. # Constants for comparing the local package file with the version in the cache
  32. (VERSION_NONE, VERSION_OUTDATED, VERSION_SAME, VERSION_NEWER) = range(4)
  33.  
  34.  
  35. class NoDebArchiveException(IOError):
  36.     """Exception which is raised if a file is no Debian archive."""
  37.  
  38.  
  39. class DebPackage(object):
  40.     """A Debian Package (.deb file)."""
  41.  
  42.     _supported_data_members = ("data.tar.gz", "data.tar.bz2", "data.tar.lzma")
  43.  
  44.     debug = 0
  45.  
  46.     def __init__(self, filename=None, cache=None):
  47.         self._cache = cache
  48.         self._need_pkgs = []
  49.         self._sections = {}
  50.         self._installed_conflicts = set()
  51.         self._failure_string = ""
  52.         if filename:
  53.             self.open(filename)
  54.  
  55.     def open(self, filename):
  56.         " open given debfile "
  57.         self.filename = filename
  58.         if not apt_inst.arCheckMember(open(self.filename), "debian-binary"):
  59.             raise NoDebArchiveException(_("This is not a valid DEB archive, "
  60.                                           "missing '%s' member" %
  61.                                           "debian-binary"))
  62.         control = apt_inst.debExtractControl(open(self.filename))
  63.         self._sections = apt_pkg.ParseSection(control)
  64.         self.pkgname = self._sections["Package"]
  65.  
  66.     def __getitem__(self, key):
  67.         return self._sections[key]
  68.  
  69.     @property
  70.     def filelist(self):
  71.         """return the list of files in the deb."""
  72.         files = []
  73.  
  74.         def extract_cb(what, name, *_):
  75.             files.append(name)
  76.  
  77.         for member in self._supported_data_members:
  78.             if apt_inst.arCheckMember(open(self.filename), member):
  79.                 try:
  80.                     apt_inst.debExtract(open(self.filename), extract_cb,
  81.                                         member)
  82.                     break
  83.                 except SystemError:
  84.                     return [_("List of files for '%s'could not be read" %
  85.                               self.filename)]
  86.         return files
  87.  
  88.     def _is_or_group_satisfied(self, or_group):
  89.         """Return True if at least one dependency of the or-group is satisfied.
  90.  
  91.         This method gets an 'or_group' and analyzes if at least one dependency
  92.         of this group is already satisfied.
  93.         """
  94.         self._dbg(2, "_checkOrGroup(): %s " % (or_group))
  95.  
  96.         for dep in or_group:
  97.             depname = dep[0]
  98.             ver = dep[1]
  99.             oper = dep[2]
  100.  
  101.             # check for virtual pkgs
  102.             if not depname in self._cache:
  103.                 if self._cache.isVirtualPackage(depname):
  104.                     self._dbg(3, "_isOrGroupSatisfied(): %s is virtual dep" %
  105.                                  depname)
  106.                     for pkg in self._cache.getProvidingPackages(depname):
  107.                         if pkg.isInstalled:
  108.                             return True
  109.                 continue
  110.  
  111.             inst = self._cache[depname]
  112.             instver = inst.installedVersion
  113.             if instver is not None and apt_pkg.CheckDep(instver, oper, ver):
  114.                 return True
  115.         return False
  116.  
  117.     def _satisfy_or_group(self, or_group):
  118.         """Try to satisfy the or_group."""
  119.         for dep in or_group:
  120.             depname, ver, oper = dep
  121.  
  122.             # if we don't have it in the cache, it may be virtual
  123.             if not depname in self._cache:
  124.                 if not self._cache.isVirtualPackage(depname):
  125.                     continue
  126.                 providers = self._cache.getProvidingPackages(depname)
  127.                 # if a package just has a single virtual provider, we
  128.                 # just pick that (just like apt)
  129.                 if len(providers) != 1:
  130.                     continue
  131.                 depname = providers[0].name
  132.  
  133.             # now check if we can satisfy the deps with the candidate(s)
  134.             # in the cache
  135.             pkg = self._cache[depname]
  136.             cand = self._cache._depcache.GetCandidateVer(pkg._pkg)
  137.             if not cand:
  138.                 continue
  139.             if not apt_pkg.CheckDep(cand.VerStr, oper, ver):
  140.                 continue
  141.  
  142.             # check if we need to install it
  143.             self._dbg(2, "Need to get: %s" % depname)
  144.             self._need_pkgs.append(depname)
  145.             return True
  146.  
  147.         # if we reach this point, we failed
  148.         or_str = ""
  149.         for dep in or_group:
  150.             or_str += dep[0]
  151.             if dep != or_group[-1]:
  152.                 or_str += "|"
  153.         self._failure_string += _("Dependency is not satisfiable: %s\n" %
  154.                                  or_str)
  155.         return False
  156.  
  157.     def _check_single_pkg_conflict(self, pkgname, ver, oper):
  158.         """Return True if a pkg conflicts with a real installed/marked pkg."""
  159.         # FIXME: deal with conflicts against its own provides
  160.         #        (e.g. Provides: ftp-server, Conflicts: ftp-server)
  161.         self._dbg(3, "_checkSinglePkgConflict() pkg='%s' ver='%s' oper='%s'" %
  162.                      (pkgname, ver, oper))
  163.  
  164.         pkg = self._cache[pkgname]
  165.         if pkg.isInstalled:
  166.             pkgver = pkg.installedVersion
  167.         elif pkg.markedInstall:
  168.             pkgver = pkg.candidateVersion
  169.         else:
  170.             return False
  171.         #print "pkg: %s" % pkgname
  172.         #print "ver: %s" % ver
  173.         #print "pkgver: %s " % pkgver
  174.         #print "oper: %s " % oper
  175.         if (apt_pkg.CheckDep(pkgver, oper, ver) and not
  176.             self.replaces_real_pkg(pkgname, oper, ver)):
  177.             self._failure_string += _("Conflicts with the installed package "
  178.                                      "'%s'" % pkg.name)
  179.             return True
  180.         return False
  181.  
  182.     def _check_conflicts_or_group(self, or_group):
  183.         """Check the or-group for conflicts with installed pkgs."""
  184.         self._dbg(2, "_check_conflicts_or_group(): %s " % (or_group))
  185.  
  186.         or_found = False
  187.         virtual_pkg = None
  188.  
  189.         for dep in or_group:
  190.             depname = dep[0]
  191.             ver = dep[1]
  192.             oper = dep[2]
  193.  
  194.             # check conflicts with virtual pkgs
  195.             if not depname in self._cache:
  196.                 # FIXME: we have to check for virtual replaces here as
  197.                 #        well (to pass tests/gdebi-test8.deb)
  198.                 if self._cache.isVirtualPackage(depname):
  199.                     for pkg in self._cache.getProvidingPackages(depname):
  200.                         self._dbg(3, "conflicts virtual check: %s" % pkg.name)
  201.                         # P/C/R on virtal pkg, e.g. ftpd
  202.                         if self.pkgname == pkg.name:
  203.                             self._dbg(3, "conflict on self, ignoring")
  204.                             continue
  205.                         if self._check_single_pkg_conflict(pkg.name, ver, oper):
  206.                             self._installed_conflicts.add(pkg.name)
  207.                 continue
  208.             if self._check_single_pkg_conflict(depname, ver, oper):
  209.                 self._installed_conflicts.add(depname)
  210.         return bool(self._installed_conflicts)
  211.  
  212.     @property
  213.     def conflicts(self):
  214.         """List of package names conflicting with this package."""
  215.         key = "Conflicts"
  216.         try:
  217.             return apt_pkg.ParseDepends(self._sections[key])
  218.         except KeyError:
  219.             return []
  220.  
  221.     @property
  222.     def depends(self):
  223.         """List of package names on which this package depends on."""
  224.         depends = []
  225.         # find depends
  226.         for key in "Depends", "PreDepends":
  227.             try:
  228.                 depends.extend(apt_pkg.ParseDepends(self._sections[key]))
  229.             except KeyError:
  230.                 pass
  231.         return depends
  232.  
  233.     @property
  234.     def provides(self):
  235.         """List of virtual packages which are provided by this package."""
  236.         key = "Provides"
  237.         try:
  238.             return apt_pkg.ParseDepends(self._sections[key])
  239.         except KeyError:
  240.             return []
  241.  
  242.     @property
  243.     def replaces(self):
  244.         """List of packages which are replaced by this package."""
  245.         key = "Replaces"
  246.         try:
  247.             return apt_pkg.ParseDepends(self._sections[key])
  248.         except KeyError:
  249.             return []
  250.  
  251.     def replaces_real_pkg(self, pkgname, oper, ver):
  252.         """Return True if a given non-virtual package is replaced.
  253.  
  254.         Return True if the deb packages replaces a real (not virtual)
  255.         packages named (pkgname, oper, ver).
  256.         """
  257.         self._dbg(3, "replacesPkg() %s %s %s" % (pkgname, oper, ver))
  258.         pkg = self._cache[pkgname]
  259.         if pkg.isInstalled:
  260.             pkgver = pkg.installedVersion
  261.         elif pkg.markedInstall:
  262.             pkgver = pkg.candidateVersion
  263.         else:
  264.             pkgver = None
  265.         for or_group in self.replaces:
  266.             for (name, ver, oper) in or_group:
  267.                 if (name == pkgname and apt_pkg.CheckDep(pkgver, oper, ver)):
  268.                     self._dbg(3, "we have a replaces in our package for the "
  269.                                  "conflict against '%s'" % (pkgname))
  270.                     return True
  271.         return False
  272.  
  273.     def check_conflicts(self):
  274.         """Check if there are conflicts with existing or selected packages.
  275.  
  276.         Check if the package conflicts with a existing or to be installed
  277.         package. Return True if the pkg is OK.
  278.         """
  279.         res = True
  280.         for or_group in self.conflicts:
  281.             if self._check_conflicts_or_group(or_group):
  282.                 #print "Conflicts with a exisiting pkg!"
  283.                 #self._failure_string = "Conflicts with a exisiting pkg!"
  284.                 res = False
  285.         return res
  286.  
  287.     def compare_to_version_in_cache(self, use_installed=True):
  288.         """Compare the package to the version available in the cache.
  289.  
  290.         Checks if the package is already installed or availabe in the cache
  291.         and if so in what version, returns one of (VERSION_NONE,
  292.         VERSION_OUTDATED, VERSION_SAME, VERSION_NEWER).
  293.         """
  294.         self._dbg(3, "compareToVersionInCache")
  295.         pkgname = self._sections["Package"]
  296.         debver = self._sections["Version"]
  297.         self._dbg(1, "debver: %s" % debver)
  298.         if pkgname in self._cache:
  299.             if use_installed:
  300.                 cachever = self._cache[pkgname].installedVersion
  301.             else:
  302.                 cachever = self._cache[pkgname].candidateVersion
  303.             if cachever is not None:
  304.                 cmp = apt_pkg.VersionCompare(cachever, debver)
  305.                 self._dbg(1, "CompareVersion(debver,instver): %s" % cmp)
  306.                 if cmp == 0:
  307.                     return VERSION_SAME
  308.                 elif cmp < 0:
  309.                     return VERSION_NEWER
  310.                 elif cmp > 0:
  311.                     return VERSION_OUTDATED
  312.         return VERSION_NONE
  313.  
  314.     def check(self):
  315.         """Check if the package is installable."""
  316.         self._dbg(3, "checkDepends")
  317.  
  318.         # check arch
  319.         arch = self._sections["Architecture"]
  320.         if  arch != "all" and arch != apt_pkg.Config.Find("APT::Architecture"):
  321.             self._dbg(1, "ERROR: Wrong architecture dude!")
  322.             self._failure_string = _("Wrong architecture '%s'" % arch)
  323.             return False
  324.  
  325.         # check version
  326.         if self.compare_to_version_in_cache() == VERSION_OUTDATED:
  327.             # the deb is older than the installed
  328.             self._failure_string = _("A later version is already installed")
  329.             return False
  330.  
  331.         # FIXME: this sort of error handling sux
  332.         self._failure_string = ""
  333.  
  334.         # check conflicts
  335.         if not self.check_conflicts():
  336.             return False
  337.  
  338.         # try to satisfy the dependencies
  339.         if not self._satisfy_depends(self.depends):
  340.             return False
  341.  
  342.         # check for conflicts again (this time with the packages that are
  343.         # makeed for install)
  344.         if not self.check_conflicts():
  345.             return False
  346.  
  347.         if self._cache._depcache.BrokenCount > 0:
  348.             self._failure_string = _("Failed to satisfy all dependencies "
  349.                                     "(broken cache)")
  350.             # clean the cache again
  351.             self._cache.clear()
  352.             return False
  353.         return True
  354.  
  355.     def satisfy_depends_str(self, dependsstr):
  356.         """Satisfy the dependencies in the given string."""
  357.         return self._satisfy_depends(apt_pkg.ParseDepends(dependsstr))
  358.  
  359.     def _satisfy_depends(self, depends):
  360.         """Satisfy the dependencies."""
  361.         # turn off MarkAndSweep via a action group (if available)
  362.         try:
  363.             _actiongroup = apt_pkg.GetPkgActionGroup(self._cache._depcache)
  364.         except AttributeError:
  365.             pass
  366.         # check depends
  367.         for or_group in depends:
  368.             #print "or_group: %s" % or_group
  369.             #print "or_group satified: %s" % self._is_or_group_satisfied(or_group)
  370.             if not self._is_or_group_satisfied(or_group):
  371.                 if not self._satisfy_or_group(or_group):
  372.                     return False
  373.         # now try it out in the cache
  374.         for pkg in self._need_pkgs:
  375.             try:
  376.                 self._cache[pkg].markInstall(fromUser=False)
  377.             except SystemError, e:
  378.                 self._failure_string = _("Cannot install '%s'" % pkg)
  379.                 self._cache.clear()
  380.                 return False
  381.         return True
  382.  
  383.     @property
  384.     def missing_deps(self):
  385.         """Return missing dependencies."""
  386.         self._dbg(1, "Installing: %s" % self._need_pkgs)
  387.         if self._need_pkgs is None:
  388.             self.check()
  389.         return self._need_pkgs
  390.  
  391.     @property
  392.     def required_changes(self):
  393.         """Get the changes required to satisfy the dependencies.
  394.  
  395.         Returns: a tuple with (install, remove, unauthenticated)
  396.         """
  397.         install = []
  398.         remove = []
  399.         unauthenticated = []
  400.         for pkg in self._cache:
  401.             if pkg.markedInstall or pkg.markedUpgrade:
  402.                 install.append(pkg.name)
  403.                 # check authentication, one authenticated origin is enough
  404.                 # libapt will skip non-authenticated origins then
  405.                 authenticated = False
  406.                 for origin in pkg.candidateOrigin:
  407.                     authenticated |= origin.trusted
  408.                 if not authenticated:
  409.                     unauthenticated.append(pkg.name)
  410.             if pkg.markedDelete:
  411.                 remove.append(pkg.name)
  412.         return (install, remove, unauthenticated)
  413.  
  414.     def _dbg(self, level, msg):
  415.         """Write debugging output to sys.stderr."""
  416.         if level <= self.debug:
  417.             print >> sys.stderr, msg
  418.  
  419.     def install(self, install_progress=None):
  420.         """Install the package."""
  421.         if install_progress is None:
  422.             return os.system("dpkg -i %s" % self.filename)
  423.         else:
  424.             try:
  425.                 install_progress.start_update()
  426.             except AttributeError:
  427.                 install_progress.startUpdate()
  428.             res = install_progress.run(self.filename)
  429.             try:
  430.                 install_progress.finish_update()
  431.             except AttributeError:
  432.                 install_progress.finishUpdate()
  433.             return res
  434.  
  435.  
  436. class DscSrcPackage(DebPackage):
  437.     """A locally available source package."""
  438.  
  439.     def __init__(self, filename=None, cache=None):
  440.         DebPackage.__init__(self, None, cache)
  441.         self._depends = []
  442.         self._conflicts = []
  443.         self._binaries = []
  444.         if filename is not None:
  445.             self.open(filename)
  446.  
  447.     @property
  448.     def depends(self):
  449.         """Return the dependencies of the package"""
  450.         return self._depends
  451.  
  452.     @property
  453.     def conflicts(self):
  454.         """Return the dependencies of the package"""
  455.         return self._conflicts
  456.  
  457.     def open(self, file):
  458.         """Open the package."""
  459.         depends_tags = ["Build-Depends", "Build-Depends-Indep"]
  460.         conflicts_tags = ["Build-Conflicts", "Build-Conflicts-Indep"]
  461.  
  462.         fobj = open(file)
  463.         tagfile = apt_pkg.ParseTagFile(fobj)
  464.         sec = tagfile.Section
  465.         try:
  466.             while tagfile.Step() == 1:
  467.                 for tag in depends_tags:
  468.                     if not sec.has_key(tag):
  469.                         continue
  470.                     self._depends.extend(apt_pkg.ParseSrcDepends(sec[tag]))
  471.                 for tag in conflicts_tags:
  472.                     if not sec.has_key(tag):
  473.                         continue
  474.                     self._conflicts.extend(apt_pkg.ParseSrcDepends(sec[tag]))
  475.                 if sec.has_key('Source'):
  476.                     self.pkgname = sec['Source']
  477.                 if sec.has_key('Binary'):
  478.                     self.binaries = sec['Binary'].split(', ')
  479.                 if sec.has_key('Version'):
  480.                     self._sections['Version'] = sec['Version']
  481.         finally:
  482.             del sec
  483.             del tagfile
  484.             fobj.close()
  485.  
  486.         s = _("Install Build-Dependencies for "
  487.               "source package '%s' that builds %s\n") % (self.pkgname,
  488.               " ".join(self.binaries))
  489.         self._sections["Description"] = s
  490.  
  491.     def check(self):
  492.         """Check if the package is installable.."""
  493.         if not self.check_conflicts():
  494.             for pkgname in self._installed_conflicts:
  495.                 if self._cache[pkgname]._pkg.Essential:
  496.                     raise Exception(_("An essential package would be removed"))
  497.                 self._cache[pkgname].markDelete()
  498.         # FIXME: a additional run of the checkConflicts()
  499.         #        after _satisfyDepends() should probably be done
  500.         return self._satisfy_depends(self.depends)
  501.  
  502.  
  503. def _test():
  504.     """Test function"""
  505.     from apt.cache import Cache
  506.     from apt.progress import DpkgInstallProgress
  507.  
  508.     cache = Cache()
  509.  
  510.     vp = "www-browser"
  511.     #print "%s virtual: %s" % (vp, cache.isVirtualPackage(vp))
  512.     providers = cache.getProvidingPackages(vp)
  513.     print "Providers for %s :" % vp
  514.     for pkg in providers:
  515.         print " %s" % pkg.name
  516.  
  517.     d = DebPackage(sys.argv[1], cache)
  518.     print "Deb: %s" % d.pkgname
  519.     if not d.check():
  520.         print "can't be satified"
  521.         print d._failure_string
  522.     print "missing deps: %s" % d.missing_deps
  523.     print d.required_changes
  524.  
  525.     print "Installing ..."
  526.     ret = d.install(DpkgInstallProgress())
  527.     print ret
  528.  
  529.     #s = DscSrcPackage(cache, "../tests/3ddesktop_0.2.9-6.dsc")
  530.     #s.checkDep()
  531.     #print "Missing deps: ",s.missingDeps
  532.     #print "Print required changes: ", s.requiredChanges
  533.  
  534.     s = DscSrcPackage(cache=cache)
  535.     d = "libc6 (>= 2.3.2), libaio (>= 0.3.96) | libaio1 (>= 0.3.96)"
  536.     print s._satisfy_depends(apt_pkg.ParseDepends(d))
  537.  
  538. if __name__ == "__main__":
  539.     _test()
  540.